﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UniUI.ConsoleUI
{
    public class ConsoleGrid : IConsoleFocusControl
    {
        private string _Text;
        public string Text
        {
            get { return _Text; }
            set
            {
                SetDirty();
                _Text = ConsoleScreen.TruncateString(value, MaxTextLength);
            }
        }

        public int MaxTextLength { get { return Editable ? Width - 1 : Width; } }

        public int EditCursorPosition { get; private set; }

        public bool Editable { get; private set; }

        private ConsoleColor _ForegroundColor;
        public ConsoleColor ForegroundColor
        {
            get { return _ForegroundColor; }
            set
            {
                SetDirty();
                _ForegroundColor = value;
            }
        }

        private ConsoleColor _BackgroundColor;
        public ConsoleColor BackgroundColor
        {
            get { return _BackgroundColor; }
            set
            {
                SetDirty();
                _BackgroundColor = value;
            }
        }

        public ConsoleGrid(IConsoleControl parent, bool editable)
        {
            this.ParentControl = parent;
            this.Editable = editable;

            //set default color
            if (editable)
            {
                this.BackgroundColor = ConsoleColor.DarkGray;
                this.ForegroundColor = ConsoleColor.White;
            }
            else
            {
                this.BackgroundColor = ConsoleColor.Black;
                this.ForegroundColor = ConsoleColor.Gray;
            }
        }

        public void OnKeyPress(ConsoleKeyInfo key, bool isInsertMode)
        {
            if (IsEditing)
            {
                SetDirty();
                if (key.Key == ConsoleKey.LeftArrow)
                {
                    if (EditCursorPosition > 0)
                    {
                        --EditCursorPosition;
                    }
                }
                else if (key.Key == ConsoleKey.RightArrow)
                {
                    if (EditCursorPosition < Text.Length)
                    {
                        ++EditCursorPosition;
                    }
                }
                else if (key.Key == ConsoleKey.Home)
                {
                    EditCursorPosition = 0;
                }
                else if (key.Key == ConsoleKey.End)
                {
                    EditCursorPosition = Text.Length;
                }
                else if (key.Key == ConsoleKey.Backspace)
                {
                    if (EditCursorPosition > 0)
                    {
                        Text = Text.Remove(--EditCursorPosition, 1);
                    }
                }
                else if (key.Key == ConsoleKey.Delete)
                {
                    if (EditCursorPosition < Text.Length)
                    {
                        Text = Text.Remove(EditCursorPosition, 1);
                    }
                }
                else if (IsCharAllowed(key.KeyChar))
                {
                    WriteChar(key, isInsertMode);
                }
                else if (key.Key == 0)
                {
                    WriteChar(key, isInsertMode);
                }
                else if (key.Key == ConsoleKey.Spacebar)
                {
                    WriteChar(key, isInsertMode);
                }
            }
        }

        private bool IsCharAllowed(char c)
        {
            return !Char.IsControl(c) && !Char.IsWhiteSpace(c);
        }

        private void WriteChar(ConsoleKeyInfo key, bool isInsertMode)
        {
            if (isInsertMode)
            {
                TryInsert(key.KeyChar);
            }
            else
            {
                TryReplace(key.KeyChar);
            }
        }

        private bool TestLength(string str)
        {
            return ConsoleScreen.GetStringLength(str) <= MaxTextLength;
        }

        private void TryInsert(char c)
        {
            var newStr = Text.Insert(EditCursorPosition, new string(c, 1));
            if (TestLength(newStr))
            {
                Text = newStr;
                ++EditCursorPosition;
            }
        }

        private void TryReplace(char c)
        {
            if (EditCursorPosition == Text.Length)
            {
                TryInsert(c);
                return;
            }
            var newStr = Text.Remove(EditCursorPosition, 1).Insert(EditCursorPosition, new string(c, 1));
            if (TestLength(newStr))
            {
                Text = newStr;
                ++EditCursorPosition;
            }
        }

        public bool IsEditing
        {
            get;
            private set;
        }

        public int CursorX
        {
            get { return this.GetAbsoluteLeft() + ConsoleScreen.GetStringLength(Text, EditCursorPosition); }
        }

        public int CursorY
        {
            get { return this.GetAbsoluteTop(); }
        }

        public bool ShowCursor
        {
            get { return IsEditing; }
        }

        public void GetFocus()
        {
            if (Editable)
            {
                SetDirty();
                IsEditing = true;
                EditCursorPosition = Text.Length;
            }
        }

        public void LostFocus()
        {
            if (Editable)
            {
                SetDirty();
                IsEditing = false;
            }
        }

        private int _Left;
        public int Left {
            get { return _Left; }
            set
            {
                this.InvalidateParent();
                _Left = value;
            }
        }

        private int _Top;
        public int Top
        {
            get { return _Top; }
            set
            {
                this.InvalidateParent();
                _Top = value;
            }
        }

        private int _Width;
        public int Width
        {
            get { return _Width; }
            set
            {
                SetDirty();
                if (value < _Width)
                {
                    this.InvalidateParent();
                }
                _Width = value;
            }
        }

        public int Height { get { return 1; } }

        public IConsoleControl ParentControl { get; private set; }

        public void Render(ConsoleScreen screen)
        {
            //Always update cursor (it's not updated to Console here).
            if (IsEditing)
            {
                screen.ShowFocus = true;
                screen.FocusX = CursorX;
                screen.FocusY = CursorY;
            }

            //Only draw when dirty.
            if (!dirty)
            {
                return;
            }

            dirty = false;

            int absX = this.GetAbsoluteLeft(), absY = this.GetAbsoluteTop();
            screen.ForegroundColor = ForegroundColor;
            screen.BackgroundColor = BackgroundColor;
            screen.DrawRect(absX, absY, Width, 1);
            screen.DrawString(absX, absY, Text);
        }

        public bool CanGetFocus
        {
            get { return Editable; }
        }

        private bool dirty = true;

        private void SetDirty()
        {
            dirty = true;
        }

        public void Invalidate(int left, int top, int width, int height)
        {
            SetDirty();
        }
    }
}
